package org.seed.rpc.clientstub;


import lombok.extern.slf4j.Slf4j;
import org.objenesis.instantiator.util.ClassUtils;
import org.seed.rpc.clientstub.util.IDWorker;
import org.seed.rpc.common.io.RpcRequest;
import org.seed.rpc.common.io.RpcResponse;
import org.seed.rpc.core.routing.RoutingStrategy;
import org.seed.rpc.core.RpcService;
import org.springframework.util.Assert;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * RPC代理实体创建
 *
 * <p>工具类</p>
 */
@Slf4j
public class RpcProxy {


    public static <T> T create(final Class<?> interfaceClass) {
        // 创建动态代理对象
        final RpcService annotation = interfaceClass.getAnnotation(RpcService.class);
        Assert.notNull(annotation, "rpc-interface must has annotation `@RpcService`, but found nothing.");
        final String serviceName = annotation.serviceName();
        final Class<?> clazzRoutingStrategy = annotation.routingStrategy();
        RoutingStrategy routingStrategy = (RoutingStrategy) ClassUtils.newInstance(clazzRoutingStrategy);
        final String interfaceName = interfaceClass.getName();
        log.debug("proxy create for interface: {}", interfaceName);
        final long requestTimeout = annotation.requestTimeout();
        return (T) Proxy.newProxyInstance(
                interfaceClass.getClassLoader(),
                new Class<?>[]{interfaceClass},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 创建 RPC 请求对象并设置请求属性
                        final RpcRequest request = new RpcRequest();
                        request.setRequestId(IDWorker.nextId62WithRand());
                        request.setInterfaceName(interfaceName);
                        request.setMethodName(method.getName());
                        request.setParameterTypes(method.getParameterTypes());
                        request.setParameters(args);
                        // 创建 RPC 客户端对象并发送 RPC 请求
                        long time = System.currentTimeMillis();
                        RpcResponse response = RpcClient.newClientSend(serviceName, routingStrategy, request, requestTimeout);
                        log.debug("{} - time used: {} ms", method.getName(), System.currentTimeMillis() - time);
                        if (response == null) {
                            throw new RuntimeException(method.getDeclaringClass().getName() + "." + method.getName() + " - response is null");
                        }
                        // 返回 RPC 响应结果
                        if (response.hasException()) {
                            throw response.getException();
                        } else {
                            return response.getResult();
                        }
                    }
                }
        );
    }


}
